
{***********************************************************}
{                                                           }
{      Informix Data Access Components                      }
{                                                           }
{      Smart Large Object support classes                   }
{                                                           }
{      Copyright (c) 2001-2008 Luxena Software Company      }
{      http://www.luxena.com                                }
{      e-mail: support@luxena.com                           }
{                                                           }
{***********************************************************}

unit IfxLob;

{$I lx.inc}

interface

uses
  Classes, IfxTypes, IfxInt8, IfxLocator, IfxConnection;


const
  IFX_STREAM_BUFFER_SIZE = $100000;


type

{ IIfxLobStorageParams }

  IIfxLobStorageParams = interface
    ['{D3034497-956C-4BA9-BA54-6D61C7772380}']

    function GetHandle: PIfx_lo_create_spec;
    function GetBytesEstimated: TIfx_int8;
    procedure SetBytesEstimated(const Value: TIfx_int8);
    function GetMaximumBytes: TIfx_int8;
    procedure SetMaximumBytes(const Value: TIfx_int8);
    function GetAllocationExtentSize: mint;
    procedure SetAllocationExtentSize(const Value: mint);
    function GetSBSpace: string;
    procedure SetSBSpace(const Value: string);
    function GetFlags: mint;
    procedure SetFlags(const Value: mint);

    procedure AssignColumnParams(Connection: TIfxConnection; const ColumnName: string);
    property Handle: PIfx_lo_create_spec read GetHandle;
    property BytesEstimated: TIfx_int8 read GetBytesEstimated write SetBytesEstimated;
    property MaximumBytes: TIfx_int8 read GetMaximumBytes write SetMaximumBytes;
    property AllocationExtentSize: mint read GetAllocationExtentSize write SetAllocationExtentSize;
    property SBSpace: string read GetSBSpace write SetSBSpace;
    property Flags: mint read GetFlags write SetFlags;
  end;


{ IIfxLobStatus }

  IIfxLobStatus = interface
    ['{7A49A84F-E517-48E1-88D2-E472ED2E6122}']

    function GetHandle: PIfx_lo_stat;
    function GetLastAccessTime: mint;
    function GetLastStatusChangeTime: mint;
    function GetLastModificationTime: mint;
    function GetReferenceCount: mint;
    function GetSize: TIfx_int8;
    function GetStorageParams: IIfxLobStorageParams;

    property Handle: PIfx_lo_stat read GetHandle;
    property LastAccessTime: mint read GetLastAccessTime;
    property LastStatusChangeTime: mint read GetLastStatusChangeTime;
    property LastModificationTime: mint read GetLastModificationTime;
    property ReferenceCount: mint read GetReferenceCount;
    property Size: TIfx_int8 read GetSize;
    property StorageParams: IIfxLobStorageParams read GetStorageParams;
  end;


{ IIfxLob }

  TIfxBufferSizeFunc = function (var Buffer; Size: Integer): Pointer;

  IIfxLob = interface
    ['{C38356B6-0BDF-48D1-B833-5221947C22BB}']

    function GetConnection: TIfxConnection;
    function GetHandle: PIfx_lo;
    function GetIsOpened: Boolean;
    function GetPosition: TIfx_int8;
    procedure SetPosition(const Value: TIfx_int8);

    function ObtainStatus: IIfxLobStatus;
    procedure AlterStorageParams(const Value: IIfxLobStorageParams);

    function SaveToFile(const FileName: string; Flags: mint = LO_CLIENT_FILE): string;
    procedure LoadFromFile(const FileName: string; Flags: mint = LO_CLIENT_FILE);
    function SaveToBuffer(Size: mint; Buffer: PChar): mint; overload;
    function SaveToBuffer(var Buffer; SizeFunc: TIfxBufferSizeFunc): mint; overload;
    procedure LoadFromBuffer(Size: mint; Buffer: PChar);
    procedure SaveToStream(Stream: TStream; BufferSize: Integer = IFX_STREAM_BUFFER_SIZE);
    procedure LoadFromStream(Stream: TStream; BufferSize: Integer = IFX_STREAM_BUFFER_SIZE);

    function GetUsedFileName(const FileName: string): string;

    procedure Open(Flags: mint);
    procedure Close;
    function Read(Buffer: PChar; Size: mint): mint;
    function ReadWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
      Whence: mint): mint;
    { first letter is lowered to remove issue with IUnknown.Release in C++Builder }
    procedure release;
    function Seek(const Offset: TIfx_int8; Whence: mint): TIfx_int8;
    procedure Truncate(const Offset: TIfx_int8; UpdateStorageParams: Boolean = False);
    function Write(Buffer: PChar; Size: mint): mint;
    function WriteWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
      Whence: mint): mint;
    procedure WriteFromFile(const FileName: string; Flags: mint = LO_CLIENT_FILE);

    procedure Lock(const Offset: TIfx_int8; Whence: mint; const Range: TIfx_int8; LockMode: mint);
    procedure Unlock(const Offset: TIfx_int8; Whence: mint; const Range: TIfx_int8);

    property Connection: TIfxConnection read GetConnection;
    property Handle: PIfx_lo read GetHandle;
    property IsOpened: Boolean read GetIsOpened;
    property Position: TIfx_int8 read GetPosition write SetPosition;
  end;


{ TIfxLobStorageParams }

  TIfxLobStorageParams = class
  private
    FHandle: PIfx_lo_create_spec;

    function GetHandle: PIfx_lo_create_spec;
    function GetBytesEstimated: TIfx_int8;
    procedure SetBytesEstimated(const Value: TIfx_int8);
    function GetMaximumBytes: TIfx_int8;
    procedure SetMaximumBytes(const Value: TIfx_int8);
    function GetAllocationExtentSize: mint;
    procedure SetAllocationExtentSize(const Value: mint);
    function GetSBSpace: string;
    procedure SetSBSpace(const Value: string);
    function GetFlags: mint;
    procedure SetFlags(const Value: mint);

  public
    constructor Create(Handle: PIfx_lo_create_spec);

    procedure AssignColumnParams(Connection: TIfxConnection; const ColumnName: string);

    property Handle: PIfx_lo_create_spec read FHandle;
    property BytesEstimated: TIfx_int8 read GetBytesEstimated write SetBytesEstimated;
    property MaximumBytes: TIfx_int8 read GetMaximumBytes write SetMaximumBytes;
    property AllocationExtentSize: mint read GetAllocationExtentSize write SetAllocationExtentSize;
    property SBSpace: string read GetSBSpace write SetSBSpace;
    property Flags: mint read GetFlags write SetFlags;
  end;


{ TIfxDefaultLobStorageParams }

  TIfxDefaultLobStorageParams = class(TIfxLobStorageParams, IIfxLobStorageParams)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    constructor Create;
    destructor Destroy; override;
    procedure BeforeDestruction; override;
    property RefCount: Integer read FRefCount;
  end;


{ TIfxLobStatusStorageParams }

  TIfxLobStatusStorageParams = class(TIfxLobStorageParams, IIfxLobStorageParams)
  protected
    FStatus: Pointer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    constructor Create(const Status: IIfxLobStatus);
  end;


{ TIfxLobStatus }

  TIfxLobStatus = class(TInterfacedObject, IIfxLobStatus)
  private
    FHandle: PIfx_lo_stat;
    FStorageParams: TIfxLobStatusStorageParams;

    function GetHandle: PIfx_lo_stat;
    function GetLastAccessTime: mint;
    function GetLastStatusChangeTime: mint;
    function GetLastModificationTime: mint;
    function GetReferenceCount: mint;
    function GetSize: TIfx_int8;
    function GetStorageParams: IIfxLobStorageParams;

  public
    constructor Create(Handle: PIfx_lo_stat);
    destructor Destroy; override;

    property Handle: PIfx_lo_stat read FHandle;
    property LastAccessTime: mint read GetLastAccessTime;
    property LastStatusChangeTime: mint read GetLastStatusChangeTime;
    property LastModificationTime: mint read GetLastModificationTime;
    property ReferenceCount: mint read GetReferenceCount;
    property Size: TIfx_int8 read GetSize;
    property StorageParams: IIfxLobStorageParams read GetStorageParams;
  end;


{ TIfxLob }

  TIfxLob = class(TInterfacedObject, IIfxLob)
  private
    FConnection: TIfxConnection;
    FHandle: TIfx_lo;
    FDescriptor: mint;

    function GetConnection: TIfxConnection;
    function GetHandle: PIfx_lo;
    function GetIsOpened: Boolean;
    function InternalGetPosition: TIfx_int8;
    function GetPosition: TIfx_int8;
    procedure InternalSetPosition(const Value: TIfx_int8);
    procedure SetPosition(const Value: TIfx_int8);

    procedure CheckOpened;
    procedure CheckClosed;

    procedure InternalCreate(Connection: TIfxConnection;
      const StorageParams: IIfxLobStorageParams; Flags: mint);
    procedure InternalOpen(Flags: mint);
    procedure InternalClose;
    function InternalObtainStatus: IIfxLobStatus;
    function InternalReadWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
      Whence: mint): mint;
    procedure InternalTruncate(const Offset: TIfx_int8; UpdateStorageParams: Boolean);

  public
    constructor Create; overload;
    constructor Create(Connection: TIfxConnection; Flags: mint = LO_RDWR); overload;
    constructor Create(Connection: TIfxConnection; const ColumnName: string; Flags: mint = LO_RDWR); overload;
    constructor Create(Connection: TIfxConnection; const StorageParams: IIfxLobStorageParams; Flags: mint = LO_RDWR); overload;
    constructor Create(Connection: TIfxConnection; const Handle: TIfx_lo); overload;
    destructor Destroy; override;

    function ObtainStatus: IIfxLobStatus;
    procedure AlterStorageParams(const StorageParams: IIfxLobStorageParams);

    function SaveToFile(const FileName: string; Flags: mint = LO_CLIENT_FILE): string;
    procedure LoadFromFile(const FileName: string; Flags: mint = LO_CLIENT_FILE);
    function SaveToBuffer(Size: mint; Buffer: PChar): mint; overload;
    function SaveToBuffer(var Buffer; SizeFunc: TIfxBufferSizeFunc): mint; overload;
    procedure LoadFromBuffer(Size: mint; Buffer: PChar);
    procedure SaveToStream(Stream: TStream; BufferSize: Integer = IFX_STREAM_BUFFER_SIZE);
    procedure LoadFromStream(Stream: TStream; BufferSize: Integer = IFX_STREAM_BUFFER_SIZE);

    function GetUsedFileName(const FileName: string): string;

    procedure Open(Flags: mint);
    procedure Close;
    function Read(Buffer: PChar; Size: mint): mint;
    function ReadWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
      Whence: mint): mint;
    procedure Release;
    function Seek(const Offset: TIfx_int8; Whence: mint): TIfx_int8;
    procedure Truncate(const Offset: TIfx_int8; UpdateStorageParams: Boolean = False);
    function Write(Buffer: PChar; Size: mint): mint;
    function WriteWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
      Whence: mint): mint;
    procedure WriteFromFile(const FileName: string; Flags: mint = LO_CLIENT_FILE);

    procedure Lock(const Offset: TIfx_int8; Whence: mint; const Range: TIfx_int8; LockMode: mint);
    procedure Unlock(const Offset: TIfx_int8; Whence: mint; const Range: TIfx_int8);

    property Connection: TIfxConnection read FConnection;
    property Handle: PIfx_lo read GetHandle;
    property IsOpened: Boolean read GetIsOpened;
    property Position: TIfx_int8 read GetPosition write SetPosition;
  end;


{ TIfxLobStream }

  TIfxLobStream = class(TStream)
  private
    FLob: IIfxLob;
    procedure SetSize8(const NewSize: TIfx_int8);
  protected
    procedure SetSize(NewSize: Longint); overload; override;
{$IFDEF D7}
    function GetSize: Int64; override;
{$ENDIF}
{$IFDEF D6}
    procedure SetSize(const NewSize: Int64); overload; override;
{$ENDIF}
  public
    constructor Create(const Lob: IIfxLob);
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
{$IFDEF D6}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
{$ENDIF}
    property Lob: IIfxLob read FLob;
  end;


{ Buffer Size Fucntions }

function SetByteArraySize(var Buffer; Size: Integer): Pointer;
function SetStringSize(var Buffer; Size: Integer): Pointer;


{$IFDEF D6}
const
  SeekOriginToWhence: array [TSeekOrigin] of mint = (LO_SEEK_SET, LO_SEEK_CUR, LO_SEEK_END);
{$ENDIF}


implementation

uses
  SysConst, SysUtils, Windows, IfxConsts, Ifx, IfxIntf, IfxConvert;


{ TIfxLobStorageParams }

constructor TIfxLobStorageParams.Create(Handle: PIfx_lo_create_spec);
begin
  if Handle = nil then
    IDACError(SInvalidStorageParamsHandle);
  FHandle := Handle;
end;

procedure TIfxLobStorageParams.AssignColumnParams(Connection: TIfxConnection;
  const ColumnName: string);
begin
  CheckConnection(Connection).MakeCurrent;
  CheckInformixResult(ifx_lo_col_info(PChar(ColumnName), FHandle));
end;

function TIfxLobStorageParams.GetHandle: PIfx_lo_create_spec;
begin
  Result := FHandle;
end;

function TIfxLobStorageParams.GetBytesEstimated: TIfx_int8;
begin
  CheckInformixResult(ifx_lo_specget_estbytes(FHandle, @Result));
end;

procedure TIfxLobStorageParams.SetBytesEstimated(const Value: TIfx_int8);
begin
  CheckInformixResult(ifx_lo_specset_estbytes(FHandle, @Value));
end;

function TIfxLobStorageParams.GetMaximumBytes: TIfx_int8;
begin
  CheckInformixResult(ifx_lo_specget_maxbytes(FHandle, @Result));
end;

procedure TIfxLobStorageParams.SetMaximumBytes(const Value: TIfx_int8);
begin
  CheckInformixResult(ifx_lo_specset_maxbytes(FHandle, @Value));
end;

function TIfxLobStorageParams.GetAllocationExtentSize: mint;
begin
  Result := ifx_lo_specget_extsz(FHandle);
  CheckInformixResult(Result);
end;

procedure TIfxLobStorageParams.SetAllocationExtentSize(const Value: mint);
begin
  CheckInformixResult(ifx_lo_specset_extsz(FHandle, Value));
end;

function TIfxLobStorageParams.GetSBSpace: string;
var
  Buffer: array [0..MAX_IDENTIFIER_LENGTH] of Char;
begin
  CheckInformixResult(ifx_lo_specget_sbspace(FHandle, Buffer, SizeOf(Buffer) - 1));
  Result := TrimRight(Buffer);
end;

procedure TIfxLobStorageParams.SetSBSpace(const Value: string);
begin
  CheckInformixResult(ifx_lo_specset_sbspace(FHandle, PChar(Value)));
end;

function TIfxLobStorageParams.GetFlags: mint;
begin
  Result := ifx_lo_specget_flags(FHandle);
  CheckInformixResult(Result);
end;

procedure TIfxLobStorageParams.SetFlags(const Value: mint);
begin
  CheckInformixResult(ifx_lo_specset_flags(FHandle, Value));
end;


{ TIfxDefaultLobStorageParams }

constructor TIfxDefaultLobStorageParams.Create;
begin
  CheckInformixResult(ifx_lo_def_create_spec(@FHandle));
end;

destructor TIfxDefaultLobStorageParams.Destroy;
begin
  if FHandle <> nil then
    CheckInformixResult(ifx_lo_spec_free(FHandle));
  inherited;
end;

function TIfxDefaultLobStorageParams.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;

function TIfxDefaultLobStorageParams._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TIfxDefaultLobStorageParams._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

procedure TIfxDefaultLobStorageParams.BeforeDestruction;
begin
  if FRefCount <> 0 then
    IDACError(SInvalidPointer);
end;


{ TIfxLobStatusStorageParams }

constructor TIfxLobStatusStorageParams.Create(const Status: IIfxLobStatus);
begin
  inherited Create(ifx_lo_stat_cspec(Status.Handle));
  FStatus := Pointer(Status);
end;

function TIfxLobStatusStorageParams.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;

function TIfxLobStatusStorageParams._AddRef: Integer;
begin
  Result := IIfxLobStatus(FStatus)._AddRef;
end;

function TIfxLobStatusStorageParams._Release: Integer;
begin
  Result := IIfxLobStatus(FStatus)._Release;
end;


{ TIfxLobStatus }

constructor TIfxLobStatus.Create(Handle: PIfx_lo_stat);
begin
  FHandle := Handle;
end;

destructor TIfxLobStatus.Destroy;
begin
  if FHandle <> nil then
    CheckInformixResult(ifx_lo_stat_free(FHandle));
  FStorageParams.Free;
  inherited;
end;

function TIfxLobStatus.GetHandle: PIfx_lo_stat;
begin
  Result := FHandle;
end;

function TIfxLobStatus.GetLastAccessTime: mint;
begin
  Result := ifx_lo_stat_atime(FHandle);
  CheckInformixResult(Result);
end;

function TIfxLobStatus.GetLastStatusChangeTime: mint;
begin
  Result := ifx_lo_stat_ctime(FHandle);
  CheckInformixResult(Result);
end;

function TIfxLobStatus.GetLastModificationTime: mint;
begin
  Result := ifx_lo_stat_mtime_sec(FHandle);
  CheckInformixResult(Result);
end;

function TIfxLobStatus.GetReferenceCount: mint;
begin
  Result := ifx_lo_stat_refcnt(FHandle);
  CheckInformixResult(Result);
end;

function TIfxLobStatus.GetSize: TIfx_int8;
begin
  CheckInformixResult(ifx_lo_stat_size(FHandle, @Result));
end;

function TIfxLobStatus.GetStorageParams: IIfxLobStorageParams;
begin
  if FStorageParams = nil then
    FStorageParams := TIfxLobStatusStorageParams.Create(Self);
  Result := FStorageParams;
end;


{ TIfxLob }

constructor TIfxLob.Create;
begin
  IDACError(SInvalidConstructor);
end;

constructor TIfxLob.Create(Connection: TIfxConnection; Flags: mint);
var
  StorageParams: TIfxDefaultLobStorageParams;
begin
  CheckConnection(Connection).Activate;
  StorageParams := TIfxDefaultLobStorageParams.Create;
  try
    InternalCreate(Connection, StorageParams, Flags);
  finally
    StorageParams.Free;
  end;
end;

constructor TIfxLob.Create(Connection: TIfxConnection;
  const ColumnName: string; Flags: mint);
var
  StorageParams: TIfxDefaultLobStorageParams;
begin
  CheckConnection(Connection).Activate;
  StorageParams := TIfxDefaultLobStorageParams.Create;
  try
    StorageParams.AssignColumnParams(Connection, ColumnName);
    InternalCreate(Connection, StorageParams, Flags);
  finally
    StorageParams.Free;
  end;
end;

constructor TIfxLob.Create(Connection: TIfxConnection;
  const StorageParams: IIfxLobStorageParams; Flags: mint);
begin
  InternalCreate(CheckConnection(Connection), StorageParams, Flags);
end;

constructor TIfxLob.Create(Connection: TIfxConnection; const Handle: TIfx_lo);
begin
  FConnection := CheckConnection(Connection);
  FHandle := Handle;
  FDescriptor := -1;
end;

destructor TIfxLob.Destroy;
begin
  Close;
  inherited;
end;

procedure TIfxLob.InternalCreate(Connection: TIfxConnection;
  const StorageParams: IIfxLobStorageParams; Flags: mint);
var
  Error: mint;
begin
  FDescriptor := ifx_lo_create(StorageParams.Handle, Flags, @FHandle, @Error);
  if FDescriptor < 0 then CheckInformixResult(Error);
  FConnection := Connection;
end;

function TIfxLob.GetConnection: TIfxConnection;
begin
  Result := FConnection;
end;

function TIfxLob.GetHandle: PIfx_lo;
begin
  Result := @FHandle;
end;

function TIfxLob.GetIsOpened: Boolean;
begin
  Result := FDescriptor >= 0;
end;

function TIfxLob.InternalGetPosition: TIfx_int8;
begin
  CheckInformixResult(ifx_lo_tell(FDescriptor, @Result));
end;

procedure TIfxLob.InternalSetPosition(const Value: TIfx_int8);
var
  Dummy: TIfx_int8;
begin
  CheckInformixResult(ifx_lo_seek(FDescriptor, @Value, LO_SEEK_SET, @Dummy));
end;

function TIfxLob.GetPosition: TIfx_int8;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  Result := InternalGetPosition;
end;

procedure TIfxLob.SetPosition(const Value: TIfx_int8);
begin
  CheckOpened;
  FConnection.MakeCurrent;
  InternalSetPosition(Value);
end;

procedure TIfxLob.CheckOpened;
begin
  if not IsOpened then
    IDACError(SClosedLob);
end;

procedure TIfxLob.CheckClosed;
begin
  if IsOpened then
    IDACError(SOpenedLob);
end;

procedure TIfxLob.InternalOpen(Flags: mint);
var
  Error: mint;
begin
  FDescriptor := ifx_lo_open(@FHandle, Flags, @Error);
  if FDescriptor < 0 then
    CheckInformixResult(Error);
end;

procedure TIfxLob.InternalClose;
begin
  CheckInformixResult(ifx_lo_close(FDescriptor));
  FDescriptor := -1;
end;

function TIfxLob.InternalObtainStatus: IIfxLobStatus;
var
  StatHandle: PIfx_lo_stat;
begin
  CheckInformixResult(ifx_lo_stat(FDescriptor, @StatHandle));
  Result := TIfxLobStatus.Create(StatHandle);
end;

function TIfxLob.InternalReadWithSeek(Buffer: PChar; Size: mint;
  const Offset: TIfx_int8; Whence: mint): mint;
var
  Error: mint;
begin
  Result := ifx_lo_readwithseek(FDescriptor, Buffer, Size, @Offset, Whence, @Error);
  if Result < 0 then
    CheckInformixResult(Error);
end;

procedure TIfxLob.InternalTruncate(const Offset: TIfx_int8; UpdateStorageParams: Boolean);
begin
  CheckInformixResult(ifx_lo_truncate(FDescriptor, @Offset));
  if UpdateStorageParams then
    with InternalObtainStatus do
    begin
      StorageParams.BytesEstimated := Position;
      AlterStorageParams(StorageParams);
    end;
end;

function TIfxLob.ObtainStatus: IIfxLobStatus;
var
  LocalOpen: Boolean;
begin
  FConnection.MakeCurrent;
  LocalOpen := not IsOpened;
  if LocalOpen then InternalOpen(LO_WRONLY);
  try
    Result := InternalObtainStatus;
  finally
    if LocalOpen then InternalClose;
  end;
end;

procedure TIfxLob.AlterStorageParams(const StorageParams: IIfxLobStorageParams);
begin
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_alter(@FHandle, StorageParams.Handle));
end;

function TIfxLob.SaveToFile(const FileName: string; Flags: mint): string;
var
  Buffer: array [0..MAX_PATH] of Char;
begin
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_copy_to_file(@FHandle, PChar(FileName), Flags, Buffer));
  Result := Buffer;
end;

procedure TIfxLob.LoadFromFile(const FileName: string; Flags: mint);
var
  LocalOpen: Boolean;
begin
  FConnection.MakeCurrent;
  LocalOpen := not IsOpened;
  if LocalOpen then InternalOpen(LO_WRONLY);
  try
    InternalSetPosition(IntToInt8(0));
    CheckInformixResult(ifx_lo_copy_to_lo(FDescriptor, PChar(FileName), Flags));
    InternalTruncate(InternalGetPosition, True);
  finally
    if LocalOpen then InternalClose;
  end;
end;

function TIfxLob.SaveToBuffer(Size: mint; Buffer: PChar): mint;
var
  Error: mint;
begin
  FConnection.MakeCurrent;
  Result := ifx_lo_to_buffer(@FHandle, Size, @Buffer, @Error);
  if Result < 0 then
    CheckInformixResult(Error);
end;

function TIfxLob.SaveToBuffer(var Buffer; SizeFunc: TIfxBufferSizeFunc): mint;
var
  LocalOpen: Boolean;
  BlobSize: Integer;
  P: PChar;
begin
  FConnection.MakeCurrent;
  LocalOpen := not IsOpened;
  if LocalOpen then InternalOpen(LO_RDONLY);
  try
    BlobSize := Int8ToInt(InternalObtainStatus.Size);
    P := SizeFunc(Buffer, BlobSize);
    InternalSetPosition(IntToInt8(0));
    Result := InternalReadWithSeek(P, BlobSize, IntToInt8(0), LO_SEEK_SET);
  finally
    if LocalOpen then InternalClose;
  end;
end;

procedure TIfxLob.LoadFromBuffer(Size: mint; Buffer: PChar);
var
  Error: mint;
begin
  FConnection.MakeCurrent;
  if ifx_lo_from_buffer(@FHandle, Size, Buffer, @Error) < 0 then
    CheckInformixResult(Error);
end;

procedure TIfxLob.SaveToStream(Stream: TStream; BufferSize: Integer);
var
  LocalOpen: Boolean;
  Buffer: PChar;
  Error: mint;
  BytesRead: Integer;
begin
  FConnection.MakeCurrent;
  
  LocalOpen := not IsOpened;
  if LocalOpen then InternalOpen(LO_RDONLY);
  try
    InternalSetPosition(IntToInt8(0));
    
    with InternalObtainStatus do
      if Int8Cmp(IntToInt8(BufferSize), Size) = 1 then
        BufferSize := Int8ToInt(Size) + 1;
        
    GetMem(Buffer, BufferSize);
    try
      repeat
        BytesRead := ifx_lo_read(FDescriptor, Buffer, BufferSize, @Error);
        if BytesRead < 0 then
          CheckInformixResult(Error);
        Stream.Write(Buffer^, BytesRead);
      until BytesRead <> BufferSize;
    finally
      FreeMem(Buffer);
    end;
  finally
    if LocalOpen then InternalClose;
  end;
end;

procedure TIfxLob.LoadFromStream(Stream: TStream; BufferSize: Integer);
var
  LocalOpen: Boolean;
  Buffer: PChar;
  Error: mint;
  BytesRead: Integer;
  BytesWrote: Integer;
begin
  FConnection.MakeCurrent;

  LocalOpen := not IsOpened;
  if LocalOpen then InternalOpen(LO_WRONLY);
  try
    InternalSetPosition(IntToInt8(0));

    if BufferSize > Stream.Size then
      BufferSize := Stream.Size + 1;
      
    GetMem(Buffer, BufferSize);
    try
      repeat
        BytesRead := Stream.Read(Buffer^, BufferSize);
        if BytesRead > 0 then
        begin
          BytesWrote := ifx_lo_write(FDescriptor, Buffer, BytesRead, @Error);
          if BytesWrote < 0 then
            CheckInformixResult(Error);
        end;
      until BytesRead <> BufferSize;
    finally
      FreeMem(Buffer);
    end;

    InternalTruncate(InternalGetPosition, True);
  finally
    if LocalOpen then InternalClose;
  end;
end;

function TIfxLob.GetUsedFileName(const FileName: string): string;
var
  Buffer: array [0..MAX_PATH] of Char;
begin
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_filename(@FHandle, PChar(FileName), Buffer, SizeOf(Buffer) - 1));
  Result := Buffer;
end;

procedure TIfxLob.Open(Flags: mint);
begin
  CheckClosed;
  FConnection.MakeCurrent;
  InternalOpen(Flags);
end;

procedure TIfxLob.Close;
begin
  if IsOpened then
  begin
    FConnection.MakeCurrent;
    InternalClose;
  end;
end;

function TIfxLob.Read(Buffer: PChar; Size: mint): mint;
var
  Error: mint;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  Result := ifx_lo_read(FDescriptor, Buffer, Size, @Error);
  if Result < 0 then
    CheckInformixResult(Error);
end;

function TIfxLob.ReadWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
  Whence: mint): mint;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  Result := InternalReadWithSeek(Buffer, Size, Offset, Whence);
end;

procedure TIfxLob.Release;
begin
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_release(@FHandle));
end;

function TIfxLob.Seek(const Offset: TIfx_int8; Whence: mint): TIfx_int8;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_seek(FDescriptor, @Offset, Whence, @Result));
end;

procedure TIfxLob.Truncate(const Offset: TIfx_int8; UpdateStorageParams: Boolean);
begin
  CheckOpened;
  FConnection.MakeCurrent;
  InternalTruncate(Offset, UpdateStorageParams);
end;

function TIfxLob.Write(Buffer: PChar; Size: mint): mint;
var
  Error: mint;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  Result := ifx_lo_write(FDescriptor, Buffer, Size, @Error);
  if Result < 0 then
    CheckInformixResult(Error);
end;

function TIfxLob.WriteWithSeek(Buffer: PChar; Size: mint; const Offset: TIfx_int8;
  Whence: mint): mint;
var
  Error: mint;
begin
  CheckOpened;
  FConnection.MakeCurrent;
  Result := ifx_lo_writewithseek(FDescriptor, Buffer, Size, @Offset, Whence, @Error);
  if Result < 0 then
    CheckInformixResult(Error);
end;

procedure TIfxLob.WriteFromFile(const FileName: string; Flags: mint);
begin
  CheckOpened;
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_copy_to_lo(FDescriptor, PChar(FileName), Flags));
end;

procedure TIfxLob.Lock(const Offset: TIfx_int8; Whence: mint;
  const Range: TIfx_int8; LockMode: mint);
begin
  CheckOpened;
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_lock(FDescriptor, @Offset, Whence, @Range, LockMode));
end;

procedure TIfxLob.Unlock(const Offset: TIfx_int8; Whence: mint; const Range: TIfx_int8);
begin
  CheckOpened;
  FConnection.MakeCurrent;
  CheckInformixResult(ifx_lo_unlock(FDescriptor, @Offset, Whence, @Range));
end;


{ TIfxLobStream }

constructor TIfxLobStream.Create(const Lob: IIfxLob);
begin
  if Lob = nil then
    IDACError(SInvalidLob);
  FLob := Lob;
end;

procedure TIfxLobStream.SetSize8(const NewSize: TIfx_int8);
begin
  with FLob.ObtainStatus do
  begin
    StorageParams.BytesEstimated := NewSize;
    FLob.AlterStorageParams(StorageParams);
  end;
end;

procedure TIfxLobStream.SetSize(NewSize: Integer);
begin
  SetSize8(IntToInt8(NewSize));
end;

{$IFDEF D7}
function TIfxLobStream.GetSize: Int64;
begin
  Result := Int8ToInt64(FLob.ObtainStatus.Size);
end;
{$ENDIF}

{$IFDEF D6}
procedure TIfxLobStream.SetSize(const NewSize: Int64);
begin
  SetSize8(Int64ToInt8(NewSize));
end;
{$ENDIF}

function TIfxLobStream.Read(var Buffer; Count: Integer): Longint;
begin
  Result := FLob.Read(@Buffer, Count);
end;

function TIfxLobStream.Write(const Buffer; Count: Integer): Longint;
begin
  Result := FLob.Write(@Buffer, Count);
end;

function TIfxLobStream.Seek(Offset: Integer; Origin: Word): Longint;
begin
  Result := Int8ToInt(FLob.Seek(IntToInt8(Offset), Origin));
end;

{$IFDEF D6}
function TIfxLobStream.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
begin
  Result := Int8ToInt64(FLob.Seek(Int64ToInt8(Offset), SeekOriginToWhence[Origin]));
end;
{$ENDIF}


{ Buffer Size Fucntions }

function SetByteArraySize(var Buffer; Size: Integer): Pointer;
type
  TByteArray = array of Byte;
begin
  SetLength(TByteArray(Buffer), Size);
  Result := Pointer(TByteArray(Buffer));
end;

function SetStringSize(var Buffer; Size: Integer): Pointer;
begin
  SetString(string(Buffer), nil, Size);
  Result := PChar(string(Buffer));
end;


end.